/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.setup.server.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.github.yulichang.repository.JoinCrudRepository;
import com.iwindplus.boot.domain.enums.BizCodeEnum;
import com.iwindplus.boot.domain.enums.EnableStatusEnum;
import com.iwindplus.boot.domain.exception.BizException;
import com.iwindplus.mgt.domain.enums.MgtCodeEnum;
import com.iwindplus.setup.domain.dto.RegionEditDTO;
import com.iwindplus.setup.domain.dto.RegionSaveDTO;
import com.iwindplus.setup.domain.vo.RegionBaseTreeVO;
import com.iwindplus.setup.domain.vo.RegionVO;
import com.iwindplus.setup.server.mapper.RegionMapper;
import com.iwindplus.setup.server.model.RegionDO;
import com.iwindplus.setup.server.service.RegionService;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 省市区业务层接口实现类.
 *
 * @author zengdegui
 * @since 2018/9/1
 */
@Service
@CacheConfig(cacheNames = {"region"})
@Transactional(rollbackFor = Exception.class)
public class RegionServiceImpl extends JoinCrudRepository<RegionMapper, RegionDO> implements RegionService {

    @CacheEvict(allEntries = true)
    @Override
    public boolean save(RegionSaveDTO entity) {
        this.getNameIsExist(entity.getName().trim(), entity.getParentId());
        entity.setStatus(EnableStatusEnum.ENABLE);
        entity.setBuildInFlag(Boolean.FALSE);
        entity.setSeq(this.getNextSeq(entity.getParentId()));
        entity.setLevel(this.getLevel(entity.getParentId()));
        final RegionDO model = BeanUtil.copyProperties(entity, RegionDO.class);
        super.save(model);
        entity.setId(model.getId());
        return Boolean.TRUE;
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean removeByIds(List<Long> ids) {
        List<RegionDO> list = super.listByIds(ids);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        boolean match = list.stream().anyMatch(RegionDO::getBuildInFlag);
        if (Boolean.TRUE.equals(match)) {
            throw new BizException(BizCodeEnum.HAS_BUILD_IN_DATA);
        }
        // 判断是否有子集
        boolean data = SqlHelper.retBool(super.count(Wrappers.lambdaQuery(RegionDO.class)
            .in(RegionDO::getParentId, ids)));
        if (data) {
            throw new BizException(MgtCodeEnum.CHILDREN_NOT_DELETED);
        }
        super.removeByIds(ids);
        return Boolean.TRUE;
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean edit(RegionEditDTO entity) {
        RegionDO data = super.getById(entity.getId());
        if (Objects.isNull(data)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        if (Boolean.TRUE.equals(data.getBuildInFlag())) {
            throw new BizException(BizCodeEnum.BUILD_IN_DATA_NOT_OPERATE);
        }
        // 校验名称是否存在
        if (CharSequenceUtil.isNotBlank(entity.getName()) && !CharSequenceUtil.equals(data.getName(), entity.getName().trim())) {
            this.getNameIsExist(entity.getName().trim(), entity.getParentId());
        }
        if (Objects.isNull(entity.getVersion())) {
            entity.setVersion(data.getVersion());
        }
        entity.setLevel(this.getLevel(entity.getParentId()));
        final RegionDO model = BeanUtil.copyProperties(entity, RegionDO.class);
        super.updateById(model);
        return Boolean.TRUE;
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean editStatus(Long id, EnableStatusEnum status) {
        RegionDO data = super.getById(id);
        if (Objects.isNull(data)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        if (Boolean.TRUE.equals(data.getBuildInFlag())) {
            throw new BizException(BizCodeEnum.BUILD_IN_DATA_NOT_OPERATE);
        }
        if (status.equals(data.getStatus())) {
            throw new BizException(BizCodeEnum.ALREADY_OPERATED);
        }
        RegionDO entity = new RegionDO();
        entity.setId(id);
        entity.setStatus(status);
        entity.setVersion(data.getVersion());
        super.updateById(entity);
        return Boolean.TRUE;
    }

    @CacheEvict(allEntries = true)
    @Override
    public boolean editBuildIn(Long id, Boolean buildInFlag) {
        RegionDO data = super.getById(id);
        if (Objects.isNull(data)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        if (buildInFlag.equals(data.getBuildInFlag())) {
            throw new BizException(BizCodeEnum.ALREADY_OPERATED);
        }
        RegionDO param = new RegionDO();
        param.setId(id);
        param.setBuildInFlag(buildInFlag);
        param.setVersion(data.getVersion());
        super.updateById(param);
        return Boolean.TRUE;
    }

    @Cacheable(key = "#root.methodName + '_' + #p0", unless = "#result == null")
    @Override
    public List<Tree<Long>> listByEnabled(EnableStatusEnum status) {
        final LambdaQueryWrapper<RegionDO> queryWrapper = Wrappers.lambdaQuery(RegionDO.class)
            .select(RegionDO::getId, RegionDO::getName, RegionDO::getCode, RegionDO::getLevel, RegionDO::getSeq, RegionDO::getParentId)
            .orderByAsc(Arrays.asList(RegionDO::getSeq));
        if (Objects.nonNull(status)) {
            queryWrapper.eq(RegionDO::getStatus, status);
        }
        List<RegionDO> list = super.list(queryWrapper);
        List<RegionBaseTreeVO> allList = BeanUtil.copyToList(list, RegionBaseTreeVO.class);
        if (CollUtil.isEmpty(allList)) {
            return null;
        }
        return this.listTree(allList, null);
    }

    @Cacheable(key = "#root.methodName + '_' + #p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public RegionVO getDetail(Long id) {
        RegionDO data = super.getById(id);
        if (Objects.isNull(data)) {
            throw new BizException(BizCodeEnum.DATA_NOT_EXIST);
        }
        return BeanUtil.copyProperties(data, RegionVO.class);
    }

    private List<Tree<Long>> listTree(List<RegionBaseTreeVO> allList, Long rootId) {
        TreeNodeConfig config = new TreeNodeConfig();
        config.setWeightKey("seq");
        return TreeUtil.build(allList, rootId, config, (object, tree) -> {
            tree.setId(object.getId());
            tree.setParentId(object.getParentId());
            tree.setWeight(object.getSeq());
            tree.setName(object.getName());
            tree.putExtra("code", object.getCode());
            tree.putExtra("level", object.getLevel());
        });
    }

    /**
     * 检测名称是否存在.
     *
     * @param name     名称
     * @param parentId 父类主键
     */
    private void getNameIsExist(String name, Long parentId) {
        QueryWrapper<RegionDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(RegionDO::getName, name);
        if (Objects.nonNull(parentId)) {
            queryWrapper.lambda().eq(RegionDO::getParentId, parentId);
        }
        boolean result = SqlHelper.retBool(super.count(queryWrapper));
        if (Boolean.TRUE.equals(result)) {
            throw new BizException(MgtCodeEnum.NAME_EXIST);
        }
    }

    /**
     * 查询下一个排序号.
     *
     * @param parentId 父类主键
     * @return Integer
     */
    private Integer getNextSeq(Long parentId) {
        QueryWrapper<RegionDO> queryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(parentId)) {
            queryWrapper.lambda().eq(RegionDO::getParentId, parentId);
        }
        queryWrapper.select("max(seq) as seq");
        Function<Object, Integer> function = val -> Integer.valueOf(val.toString());
        Integer data = super.getObj(queryWrapper, function);
        return Optional.ofNullable(data).map(x -> x + 1).orElse(1);
    }

    /**
     * 查询级别.
     *
     * @param parentId 父类主键
     * @return Integer
     */
    private Integer getLevel(Long parentId) {
        if (Objects.isNull(parentId)) {
            return 1;
        }
        QueryWrapper<RegionDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(RegionDO::getId, parentId);
        queryWrapper.lambda().select(RegionDO::getLevel);
        Function<Object, Integer> function = val -> Integer.valueOf(val.toString());
        Integer data = super.getObj(queryWrapper, function);
        return Optional.ofNullable(data).map(x -> x + 1).orElse(1);
    }
}
